package cn.duyo.spring.beans.factory.support;

import cn.duyo.spring.beans.BeansException;
import cn.duyo.spring.beans.factory.DisposableBean;
import cn.duyo.spring.beans.factory.ObjectFactory;
import cn.duyo.spring.beans.factory.config.SingletonBeanRegistry;

import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 单例注册器
 * @author du
 */
public class DefaultSingletonBeanRegistry implements SingletonBeanRegistry {

    /**
     * Internal marker for a null singleton object:
     * used as marker value for concurrent Maps (which don't support null values).
     */
    protected static final Object NULL_OBJECT = new Object();

    // 一级缓存(成品单例对象)
    private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>();

    // 二级缓存(提前暴漏对象，属性未填充的半成品对象)
    private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>();

    // 三级缓存(存放代理对象，即单例工厂对象)
    private final Map<String, ObjectFactory<?>> singletonFactories = new ConcurrentHashMap<>();

    /**
     * TODO 为何要销毁的bean要单独放到一个集合?
     * tips: 由于实现 DisposableBean 接口或者destroy方法的对象一般是 线程池、IO连接池、网络连接池等，并非所有bean都需要优雅关闭。
     *       比如数据库连接池若未关闭，但JVM进程可能正在关闭，会造成可能正在提交分布式事务失败。即涉及到 SpringBoot 的优雅关机
     *       SpringBoot在2.3.x实现了真正意义的优雅关机，之前的版本由于内置的 tomcat、jetty等内嵌服务器并未内置优雅关机所以无法支持。
     *       server.shutdown=graceful (支持 kill -2 / kill -15)
     *       spring.lifecycle.timeout-per-shutdown-phase=30s (指定了等待活跃请求完成的最大时间，默认为30秒‌)
     */
    private final Map<String, DisposableBean> disposableBeans = new LinkedHashMap<>();

    @Override
    public Object getSingleton(String beanName) {
        // 直接获取无法解决循环依赖
        //return singletonObjects.get(beanName);

        // TODO 此处要解决循环依赖，一级缓存——>二级缓存——>三级缓存 ，逐层获取直到拿到有效对象
        Object singletonObject = singletonObjects.get(beanName);
        if (null == singletonObject) {
            singletonObject = earlySingletonObjects.get(beanName);
            // 判断二级缓存中是否有对象，这个对象就是代理对象，因为只有代理对象才会放到三级缓存中
            if (null == singletonObject) {
                ObjectFactory<?> singletonFactory = singletonFactories.get(beanName);
                if (singletonFactory != null) {
                    // TODO 会触发注册的回调函数执行
                    System.out.println("1. 前两级缓存没有，触发获取三级代理类缓存,beanName="+beanName);
                    singletonObject = singletonFactory.getObject();
                    // 把三级缓存中的代理对象中的真实对象获取出来，放入二级缓存中
                    earlySingletonObjects.put(beanName, singletonObject);
                    // 三级缓存移除
                    singletonFactories.remove(beanName);
                }
            }
        }
        return singletonObject;
    }

    @Override
    public void registerSingleton(String beanName, Object singletonObject) {
        singletonObjects.put(beanName, singletonObject);
        // TODO 此时需要从二级，三级移除
        earlySingletonObjects.remove(beanName);
        singletonFactories.remove(beanName);
    }

    protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
        // TODO 代表是代理对象，需要从二级缓存移除，放到三级缓存（因为三级缓存专用于存放代理对象）
        if (!this.singletonObjects.containsKey(beanName)) {
            this.singletonFactories.put(beanName, singletonFactory);
            this.earlySingletonObjects.remove(beanName);
        }
    }

    public void registerDisposableBean(String beanName, DisposableBean bean) {
        disposableBeans.put(beanName, bean);
    }

    public void destroySingletons() {
        Set<String> keySet = this.disposableBeans.keySet();
        Object[] disposableBeanNames = keySet.toArray();

        for (int i = disposableBeanNames.length - 1; i >= 0; i--) {
            Object beanName = disposableBeanNames[i];
            DisposableBean disposableBean = disposableBeans.remove(beanName);
            try {
                disposableBean.destroy();
            } catch (Exception e) {
                throw new BeansException("Destroy method on bean with name '" + beanName + "' threw an exception", e);
            }
        }
    }
}
